package com.woniu.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.client.ProductClient;
import com.woniu.commons.exception.YogaException;
import com.woniu.commons.model.RedisKey;
import com.woniu.commons.model.StateData;
import com.woniu.commons.util.DigestUtil;
import com.woniu.commons.util.JwtUtil;
import com.woniu.commons.util.RedisUtil;
import com.woniu.dao.YUserMapper;
import com.woniu.model.dto.UserDto;
import com.woniu.model.entity.YUser;
import com.woniu.model.form.PageAllStudentForm;
import com.woniu.model.form.RealNameAuthenticationForm;
import com.woniu.model.param.*;
import com.woniu.service.YUserService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 胡歌
 * @since 2024年05月22日
 */
@Service
public class YUserServiceImpl extends ServiceImpl<YUserMapper, YUser> implements YUserService {

    @Resource
    private RedissonClient redissonClient;//通过这个完成释放锁上锁等一系列操作，导入依赖后直接使用
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private YUserMapper userMapper;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private ProductClient productClient;


    /*
    发送注册验证码的方法
     */
    @Override
    public void sendRegisterCode(String mobile) {
        //判断手机号码是否在一分钟内是否发送短信
        if (redisUtil.exist(RedisKey.registerCodeOnMinute(mobile))) {
            throw new YogaException(500,"1分钟不能再次发送");
        }
        //判断该手机号码在一小时内的短信发送次数
        if (redisUtil.exist(RedisKey.sendMessageNumber(mobile))) {
            Integer number = Integer.parseInt(redisUtil.get(RedisKey.sendMessageNumber(mobile)));
            if (number >= 5) {
                throw new YogaException(500,"超过发送短信次数,请稍后再试");
            }
        }
        //判断手机号是否注册过
        QueryWrapper<YUser> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone", mobile);

        YUser userInfo = userMapper.selectOne(wrapper);

        if (userInfo != null) {
            throw new YogaException(500,"该手机号已注册");
        }
        //将手机号码保存到消息队列中
        rabbitTemplate.convertAndSend("register_phone_queue",mobile);
    }

    @Override
    public void UpdateUserPhone(String mobile) {
        //判断手机号码是否在一分钟内是否发送短信
        if (redisUtil.exist(RedisKey.updateCodeOnMinute(mobile))) {
            throw new YogaException(500,"1分钟不能再次发送");
        }
        //判断该手机号码在一小时内的短信发送次数
        if (redisUtil.exist(RedisKey.updateMessageNumber(mobile))) {
            Integer number = Integer.parseInt(redisUtil.get(RedisKey.updateMessageNumber(mobile)));
            if (number >= 50) {
                throw new YogaException(500,"超过发送短信次数,请稍后再试");
            }
        }
        //判断手机号是否注册过
        QueryWrapper<YUser> wrapper = new QueryWrapper<>();
        wrapper.eq("user_phone", mobile);

        YUser userInfo = userMapper.selectOne(wrapper);

        if (userInfo == null) {
            throw new YogaException(600,"手机号不匹配");
        }
        //将手机号码保存到消息队列中
        rabbitTemplate.convertAndSend("update_phone_queue",mobile);
    }

    @Override
    public UserDto userlogin(UserInfoLoginParam param) {
        QueryWrapper<YUser> wrapper = new QueryWrapper<YUser>();
        wrapper.eq("user_name", param.getUserName());
        YUser user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new YogaException(5002,"尚未注册");
        }
        if (!user.getUserPass().equals(DigestUtil.md5(param.getUserPass(), param.getUserName()))) {
            throw new YogaException(5005,"密码错误");
        }
        UserDto dto = new UserDto();
        dto.setUserId(user.getUserId());
        dto.setUserName(user.getUserName());
        return dto;
    }

    @Override
    public void UpdateUserPassword(UpdateUserPasswordParam param) throws Exception {
        YUser user = userMapper.selectById(param.getUserId());
        String newPass=DigestUtil.md5(param.getUserPass(),user.getUserName());
        if (user.getUserPass().equals(newPass)){
            throw new Exception("两次密码一样，请重新输入");
        }
        user.setUserPass(newPass);
        userMapper.updateById(user);

    }

    @Override
    @Transactional
    public void codeUpdate(CodeUpdateParam param) throws Exception {

        //创建一把手机号码锁(公平锁)
        RLock phoneLock = redissonClient.getFairLock(RedisKey.phoneLock(param.getOldPhone()));
        //变成多资源锁；这里放进该方法的顺序就是加锁的顺序
        RLock lock = redissonClient.getMultiLock(phoneLock);

        if (lock.tryLock(10, TimeUnit.SECONDS)) {
            //设置10秒；代表成功
            try {//一定要用try块包括成功，finally成功与否都要释放锁
                //首先判断总错误次数
                if (redisUtil.exist(RedisKey.codeErrorNumber(param.getOldPhone()))) {
                    if (Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getOldPhone()))) >= 5) {
                        throw new Exception("超过最大次数限制，验证码失效");
                    }
                }
                //先判断验证码是否正确
//                System.out.println(param.getOldPhone()+"===========");
//                System.out.println(param.getCode());
//                System.out.println(!param.getCode().equals(redisUtil.get(RedisKey.updateCodeKey(param.getOldPhone()))));
                if (!param.getCode().equals(redisUtil.get(RedisKey.updateCodeKey(param.getOldPhone())))) {
                    //验证码错误，就给他的错误总次数+1
                    if (redisUtil.exist(RedisKey.codeErrorNumber(param.getOldPhone()))) {
                        redisUtil.incr(RedisKey.codeErrorNumber(param.getOldPhone()));
                        if (Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getOldPhone()))) >= 5) {
                            redisUtil.delete(RedisKey.updateCodeKey(param.getOldPhone()));
                        }
                    } else {
                        redisUtil.set(RedisKey.codeErrorNumber(param.getOldPhone()), "1", 5 * 60);
                    }
                    throw new Exception("验证码错误");
                }
                //判断手机号是否注册过
                QueryWrapper<YUser> wrapper = new QueryWrapper<YUser>();
                wrapper.eq("user_phone", param.getOldPhone());

                YUser userInfo = userMapper.selectOne(wrapper);
                if (userInfo == null) {
                    throw new Exception("该手机号有误");
                }
                //执行修改
                userInfo = BeanUtil.toBean(param, YUser.class);
                userInfo.setUserPhone(param.getNewPhone());
                int update = userMapper.update(userInfo, wrapper);
                if (update==0){
                    throw new Exception("手机号更新失败");
                }
                //删除验证码
                redisUtil.delete(RedisKey.registerCodeKey(param.getOldPhone()));
            } finally {
                lock.unlock();//释放锁
            }
        } else {
            //代表失败
            throw new Exception("服务器繁忙");
        }
    }

    @Override
    public boolean inspectIdCard(String IdCard) {
        int[] WEIGHTS = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        // 校验码
        char[] CHECK_CODES = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

        if (IdCard == null || IdCard.length() != 18) {
            return false;
        }

        // 校验前17位是否为数字，第18位是否为数字或'X'
        for (int i = 0; i < 17; i++) {
            if (!Character.isDigit(IdCard.charAt(i))) {
                return false;
            }
        }
        char lastChar = IdCard.charAt(17);
        if (!(Character.isDigit(lastChar) || lastChar == 'X' || lastChar == 'x')) {
            return false;
        }

        // 计算校验码
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum += (IdCard.charAt(i) - '0') * WEIGHTS[i];
        }
        char checkCode = CHECK_CODES[sum % 11];

        // 验证校验码
        return Character.toUpperCase(lastChar) == checkCode;
    }

    @Override
    public String userRealNameAuthentication(RealNameAuthenticationForm form) {
        String str = JwtUtil.realNameAuthentication(form.getCardId(), form.getUserName());
        return str.equals("实名认证通过") ? "实名认证通过" : "实名认证失败，请重试";
    }

    /**
     * 分页查询所有用户
     * @param param 分页参数
     * @return
     * @throws Exception
     */
    @Override
    public Page<YUser> pageAllUser(PageAllParam param) throws YogaException {
        if (param.getPageNum() == null) {
            param.setPageNum(1);
        }
        if (param.getPageSize() == null) {
            param.setPageSize(5);
        }
        //拼接条件
        QueryWrapper<YUser> wrapper = new QueryWrapper<YUser>();
        wrapper.like("user_name",param.getUserName());
        Page<YUser> page = new Page<>(param.getPageNum(), param.getPageSize());
        return  baseMapper.selectPage(page,wrapper);

    }

    @Override
    public Page<YUser> pageAllStudent(PageAllStudentForm form) {
        if (form.getPageNum() == null) {
            form.setPageNum(1);
        }
        if (form.getPageSize() == null) {
            form.setPageSize(5);
        }
        Page<YUser> p = new Page<>(form.getPageNum(), form.getPageSize());
        List<Integer> leagueStudents = productClient.loadLeagueStudentIds(form.getCoachId()).getData();
        List<Integer> personalStudents = productClient.loadPersonalStudentIds(form.getCoachId()).getData();
        Set<Integer> integers = new HashSet<>();
        if (leagueStudents.size()==0&&personalStudents.size()==0){
            throw new YogaException(3502,"该教练没有学员");
        }
        if (leagueStudents.size()!=0){
            integers.addAll(leagueStudents);
        }
        if (personalStudents.size()!=0){
            integers.addAll(personalStudents);
        }

        QueryWrapper<YUser> wrapper = new QueryWrapper<YUser>().in("user_id", integers).eq("user_state", 1);


        return baseMapper.selectPage(p,wrapper);
    }

    @Override
    public Page<YUser> pageAllPersonalStudent(PageAllStudentForm form) {
        if (form.getPageNum() == null) {
            form.setPageNum(1);
        }
        if (form.getPageSize() == null) {
            form.setPageSize(5);
        }
        Page<YUser> p = new Page<>(form.getPageNum(), form.getPageSize());

        Set<Integer> integers = new HashSet<>();
        List<Integer> personalStudents = productClient.loadPersonalStudentIds(form.getCoachId()).getData();
        if (personalStudents.size()!=0){
            integers.addAll(personalStudents);
        }

        QueryWrapper<YUser> wrapper = new QueryWrapper<YUser>().in("user_id", integers).eq("user_state", 1);

        return baseMapper.selectPage(p,wrapper);
    }

    @Override
    public YUser loadUsersByIds(Integer userId) {
        YUser user = baseMapper.selectOne(new QueryWrapper<YUser>().eq("user_id", userId).eq("user_state", 1));


        return user;
    }


    /*
    用户注册的方法
     */
    @Override
    public void CodeRegister(CodeRegisterParam param) throws Exception {

        //创建一把手机号码锁(公平锁)
        RLock phoneLock = redissonClient.getFairLock(RedisKey.phoneLock(param.getUserPhone()));
        //创建一把用户名锁（公平锁）
        RLock userNameLock = redissonClient.getFairLock(RedisKey.userNameLock(param.getUserName()));
        //变成多资源锁；这里放进该方法的顺序就是加锁的顺序
        RLock lock = redissonClient.getMultiLock(phoneLock, userNameLock);

        if (lock.tryLock(10, TimeUnit.SECONDS)) {
            //设置10秒；代表成功
            try {//一定要用try块包括成功，finally成功与否都要释放锁

                //首先判断总错误次数
                if (redisUtil.exist(RedisKey.codeErrorNumber(param.getUserPhone()))) {
                    if (Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getUserPhone()))) >= 5) {
                        throw new Exception("超过最大次数限制，验证码失效");
                    }
                }

                //先判断验证码是否正确
                if (!param.getCode().equals(redisUtil.get(RedisKey.registerCodeKey(param.getUserPhone())))) {
                    //验证码错误，就给他的错误总次数+1
                    if (redisUtil.exist(RedisKey.codeErrorNumber(param.getUserPhone()))) {
                        redisUtil.incr(RedisKey.codeErrorNumber(param.getUserPhone()));
                        if (Integer.parseInt(redisUtil.get(RedisKey.codeErrorNumber(param.getUserPhone()))) >= 5) {
                            redisUtil.delete(RedisKey.registerCodeKey(param.getUserPhone()));
                        }
                    } else {
                        redisUtil.set(RedisKey.codeErrorNumber(param.getUserPhone()), "1", 5 * 60);
                    }
                    throw new Exception("验证码错误");
                }
                //判断手机号是否注册过
                QueryWrapper<YUser> wrapper = new QueryWrapper<YUser>();
                wrapper.eq("user_phone", param.getUserPhone());

                YUser userInfo = userMapper.selectOne(wrapper);
                if (userInfo != null) {
                    throw new Exception("该手机号已注册");
                }
                //判断用户名是否已经被注册
                QueryWrapper<YUser> wrapper1 = new QueryWrapper<YUser>();
                wrapper1.eq("user_name", param.getUserName());
                YUser userInfo1 = userMapper.selectOne(wrapper1);
                if (userInfo1 != null) {
                    throw new Exception("用户名已注册");
                }
                //执行注册
                userInfo = BeanUtil.toBean(param, YUser.class);
                //对密码加密
                String md5 = DigestUtil.md5(param.getUserPass(), param.getUserName());
                userInfo.setUserPass(md5);
                userInfo.setUserState(1);
                userInfo.setIdCard(param.getIdCard());
                userMapper.insert(userInfo);
                //删除验证码
                redisUtil.delete(RedisKey.registerCodeKey(param.getUserPhone()));
            } finally {
                lock.unlock();//释放锁
            }

        } else {
            //代表失败
            throw new Exception("服务器繁忙");
        }
    }

    @Override
    public void updateImage(UpdateUserImageParam param) {
        YUser user = userMapper.selectById(param.getUserId());
        user.setUserImage(param.getImage());
        userMapper.updateById(user);
    }

    @Override
    public YUser UpdateNickName(UpdateNickNameParam param) {
        YUser user = new YUser();
        user.setUserNickname(param.getUserNickname());
        user.setUserId(param.getUserId());
        return null;
    }


}
